#ifndef __MOYING_COMMU_REPLY_H__
#define __MOYING_COMMU_REPLY_H__
#include <moying_comm_lib/moying_commu_base.h>
#include <moying_comm_lib/receive_callback.h>
#include <memory>

namespace moying
{
namespace communication
{

class MoyingCommuReply : public MoyingCommuBase
{
public:
  MoyingCommuReply(const std::string &url, const std::string &identity);
  MoyingCommuReply(const std::string &ip, const unsigned int port, const std::string &identity);
  ~MoyingCommuReply() noexcept;

  template<typename REQ, typename RES>
  bool advertiseService(const std::string &content, void(*rep_cb)(const REQ&, RES&), bool log = true)
  {
    if(!alterRepContent(content, true))
      return false;
    if(proto_types_.find(content) == proto_types_.end())
    {
      proto_types_[content] = std::make_pair<::google::protobuf::Message*, ::google::protobuf::Message*>(new REQ(), new RES());
    }
    rep_cbs_[content] = std::make_pair(std::make_shared<ReplyCallback<REQ, RES>>(rep_cb), log);
    return true;
  }
  
  template<typename REQ, typename RES>
  bool advertiseService(const std::string &content, std::function<void(const REQ&, RES&)> rep_cb, bool log = true)
  {
    if(!alterRepContent(content, true))
      return false;
    if(proto_types_.find(content) == proto_types_.end())
    {
      proto_types_[content] = std::make_pair<::google::protobuf::Message*, ::google::protobuf::Message*>(new REQ(), new RES());
    }
    rep_cbs_[content] = std::make_pair(std::make_shared<ReplyCallback<REQ, RES>>(rep_cb), log);
    return true;
  }

  template<typename REQ, typename RES, class T>
  bool advertiseService(const std::string &content, void(T::*rep_cb)(const REQ&, RES&), T* obj, bool log = true)
  {
    if(!alterRepContent(content, true))
      return false;
    if(proto_types_.find(content) == proto_types_.end())
    {
      proto_types_[content] = std::make_pair<::google::protobuf::Message*, ::google::protobuf::Message*>(new REQ(), new RES());
    }
    rep_cbs_[content] = std::make_pair(std::make_shared<ReplyCallback<REQ, RES>>(boost::bind(rep_cb, obj, _1, _2)), log);
    return true;
  }

  inline bool set_recv_handler_thread_number(int thread_number){
    if(thread_number <= 0)
      return false;
    else{
      recv_handler_thread_number_ = thread_number;
      return true;
    }
  }

  virtual bool run() override;
  virtual bool close() override;
private:
  void createProxy();
  void createWorker();
  void printPackWrapper(bool is_send, const std::string &content, bytes_ptr pack, int thread_id);
  bool alterRepContent(const std::string &content, bool add);
  void encodeServicePack(const std::string &content, const bytes_ptr &request, const bytes_ptr &response, bytes_ptr &pack_data);
  void decodeServicePack(const bytes_ptr &pack_data, std::string &content, bytes_ptr &request, bytes_ptr &response);

private:
//  zmq::context_t ctx_;   // 上下文环境
  zmq::socket_t router_; // 负责监听外部连接
  zmq::socket_t dealer_; // 负责转发请求消息
  zmq::socket_t ctrl_sub_; // 负责接收proxy控制指令
  zmq::socket_t ctrl_pub_; // 负责发布proxy控制指令
  std::string   worker_inproc_name_;    //工作线程和dealer通信的地址
  std::map<std::string, std::pair<ReplyCallbackBasePtr, bool>> rep_cbs_;
  std::map<std::string, std::pair<::google::protobuf::Message*, ::google::protobuf::Message*>> proto_types_;
  unsigned int recv_handler_thread_number_; //接收封包处理线程数量
};

typedef std::shared_ptr<MoyingCommuReply> MoyingCommuReplyPtr;

} // namespace communication
} // namespace moying

#endif // __MOYING_COMMU_REPLY_H__
